Skip to content

ncurses: add the terminal dependency root#8

Merged
brandonpayton merged 5 commits into
mainfrom
migrate/ncurses-dependency-root
Jul 12, 2026
Merged

ncurses: add the terminal dependency root#8
brandonpayton merged 5 commits into
mainfrom
migrate/ncurses-dependency-root

Conversation

@brandonpayton

@brandonpayton brandonpayton commented Jul 11, 2026

Copy link
Copy Markdown
Member

Purpose

Add ncurses as Kandelo's terminal-library dependency root. The formula builds the wide-character static C and C++ interfaces, debug variants, conventional compatibility names, pkg-config metadata, guest utilities, manpages, compiled fallbacks, and the full terminfo database.

This unblocks dependency-first migrations for shells, editors, terminal tools, and other CLI packages.

Platform prerequisites

Bottle publication remains gated on the open Kandelo platform chain:

  • #842 supplies the shared SDK main-thread stack needed by ncurses' nested terminfo reader frames.
  • #873 builds fork instrumentation from the repository workspace.
  • #881 removes Actions-cache consumption from the reusable publisher so read-only exact-branch dry runs cannot contaminate later publication.
  • #882 rejects stale artifacts with aliased ABI exports.
  • #885 publishes tap-native formulas without registry duplicates.
  • #890 gives the isolated bottle builder explicit trust for same-tap dependencies such as libcxx.

The pthread fork-continuation fix and ABI 18 transition from #867 are merged. The formula deliberately does not inject a package-specific stack flag. Static ncurses cannot own a final executable's stack, and doing so would hide the same platform defect from other software.

Formula contract

  • Builds wasm32 normal and debug static libraries for ncurses, terminfo, form, menu, panel, and the C++ binding.
  • Uses the tap's libcxx formula for the C++ binding.
  • Installs wide, conventional, termcap, top-level-header, and namespaced-header compatibility interfaces.
  • Builds host tic/infocmp only for architecture-independent data generation.
  • Ships more than 2,000 compiled terminal entries plus isolated compiled-in fallbacks.
  • Binds compiled terminfo lookup and config scripts to /home/linuxbrew/.linuxbrew/opt/ncurses, never the build host's keg.
  • Runs target tic, tput, and infocmp under Kandelo and verifies a generated custom terminal entry.
  • Exercises C, C++, pkg-config, termcap, header compatibility, fallback lookup, and non-fallback disk lookup paths.

Generator portability fix

Upstream MKfallback.sh uses GNU sed's word-boundary syntax to rewrite generated short arrays to NCURSES_INT2. Darwin sed silently leaves that expression unchanged. With target extended colors enabled, the resulting fallback table stored 16-bit numbers while ncurses read the TERMTYPE2 table as 32-bit numbers, so setupterm() succeeded but numeric capabilities such as colors were misindexed.

The formula rewrites that generator expression to a portable, declaration-specific substitution and rejects a generated fallback table with the wrong target width.

Reproducible debug archives

The previously reported local bottle (a6110827...) is rejected and must not be published. Its _g archives retained builder paths and nondeterministic member metadata.

The formula now:

  • maps source, SDK, libcxx, LLVM, and Nix paths to stable debug roots for both C and C++;
  • pins ncurses' archive probe to LLVM ar deterministic crD mode instead of upstream's first accepted -curvU candidate;
  • audits every installed real static archive during install and brew test, while allowing only the intentional guest opt prefix.

Validation

Local evidence using scripts/dev-shell.sh and the ABI 18 integration worktree:

  • Two independent clean source builds used distinct Homebrew roots (ncurses-20260711-23670-x5gtpv and ncurses-20260711-57816-nxor9o).
  • Full brew test passed for both builds, including isolated compiled fallback, disk database, target tic output, C, C++, pkg-config, termcap, and header compatibility paths.
  • All six debug archives were byte-identical between the tested builds. Representative libncursesw_g.a SHA-256: d3d1c0e57d265ff111dbafc414861a77d98b1a26275c265246b9a819a9fbdcd9.
  • A second exact-head pair from distinct Homebrew roots (ncurses-20260711-59925-j3iowy and ncurses-20260711-94733-8c3cqa) produced byte-identical manifests for all 12 real normal and debug archives. Representative normal libncursesw.a SHA-256: 2d6d3b92b8b9aa12908d19fc83552f89b59108ae12bb0b7c38e628ef81a73d28.
  • LLVM archive inspection showed zero UID/GID and epoch member timestamps; DWARF uses /usr/src/ncurses-6.5 as its compilation directory.
  • All 12 real static archives passed an independent binary scan for /Users/, /private/tmp/, /private/var/, /nix/store/, and the Kandelo integration worktree.
  • Corrected fallback.o uses the expected 32-bit numeric-capability layout.
  • brew style: no offenses.
  • Homebrew Ruby syntax and git diff --check: passed.

A corrected bottle will be generated from the final merged platform and publisher state.

brew audit is not claimed: the cross-target's intentional top-level compatibility headers trigger Homebrew's macOS system-header shadow rule. Publication remains gated on the platform PRs above.

Keg reload and dependency provenance

Current head 56dc558 resolves shared formula support through Tap.fetch, matching the tap-wide installed-formula contract. This fixes the prior exact-head keg reload failure caused by require_relative looking beside Homebrew's keg-local .brew/ncurses.rb copy. Through scripts/dev-shell.sh, Homebrew portable Ruby syntax, literal formula loading through brew ruby, brew style Formula/ncurses.rb, and git diff --check pass.

The archive reproducibility evidence above used a source-built libcxx dependency: its retained receipt says built_as_bottle: false, poured_from_bottle: false, and identifies an older tap head. That evidence remains valid for ncurses' own 12 deterministic archives, but it is not bottle-to-bottle dependency evidence. Publication must proceed dependency-first:

  1. Publish and merge the current ABI 18 libcxx bottle metadata.
  2. Build ncurses from this formula in a clean Homebrew prefix.
  3. Retain the libcxx install receipt proving poured_from_bottle: true with the Kandelo bottle tag and current formula provenance.
  4. Pour the resulting ncurses bottle through stock guest Homebrew and run literal brew test before publishing ncurses metadata.

The ABI 18 ledger repair and #867 are already on main. Remaining publication gates are #842, #873, #881 plus tap #22, #882, #887, #890, and #885 rebased to retain #890. #892 is not an ncurses prerequisite. The source formula can land independently; no bottle block or user-facing install claim is added by this PR.

@brandonpayton brandonpayton force-pushed the migrate/ncurses-dependency-root branch 3 times, most recently from 2a98db2 to 7b5a83d Compare July 11, 2026 04:28
@brandonpayton

Copy link
Copy Markdown
Member Author

Rebased onto current tap main through #13 to remove the README-only conflict.

  • the Formula/ncurses.rb blob is byte-for-byte unchanged (d3fc1905c1a679058311b08673636fbde72181bc before and after)
  • the README resolution preserves the ncurses, sed, gzip, and grep entries
  • Homebrew portable Ruby reports Syntax OK
  • git diff --check origin/main...HEAD passes
  • GitHub now reports the PR mergeable

No ABI 18 artifact rebuild or runtime claim was added by this rebase. The existing #842/#867 and bottle-publication gates remain in force.

@brandonpayton brandonpayton force-pushed the migrate/ncurses-dependency-root branch from 23f0533 to 93567f2 Compare July 11, 2026 22:55
Use Homebrew tap resolution so the installed keg copy of the formula can reload shared Kandelo support instead of looking for a nonexistent relative path.
@brandonpayton brandonpayton merged commit 4d3d3a0 into main Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant